/*
 * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.oracle.max.vm.ext.vma.store.txt;

import java.util.*;

/**
 * Defines a compact textual format for the the {@link VMATextStore} output.
 *
 * There is an explicit assumption that store records are ordered in time to support the relative time optimization.
 * However, an embedded {@link Key#THREAD_SWITCH} record can be used to "reset" the time for stores that are created
 * from a set of records for a set of threads. Such "batched" stores are also indicated by the second argument to the
 * {@link Key#INITIALIZE_LOG} record having the {@link BATCHED} bit set. A per-thread store contains only records from
 * one thread and is indicated by the second argument to the {@link Key#INITIALIZE_LOG} record having the
 * {@link PER_THREAD} bit set.
 *
 * Normally the store uses relative time, recording the offset from the previous record for that thread. However, it is
 * possible to use absolute time and this is indicated by a {@code true} value to first argument of the
 * {@link Key#INITIALIZE_LOG} record.
 *
 * Each store record occupies one line starting with the string code for the {@link Key}. The key is followed by the
 * time (either absolute or relative) and then, for most records, the thread that created the record, and the bytecode
 * index of the associated instruction. This is then followed by arguments that are specific to the record, generally in
 * same order as the parameters to the methods in {@link VMATextStore}.
 *
 * Short forms are used to denote class, thread, field and method names and these are defined using,
 * {@link Key#CLASS_DEFINITION} etc.
 *
 * <ul>
 * <li>T "thread name" threadShortName
 * <li>C className clId classShortName
 * <li>F classShortName fieldName fieldShortName
 * <li>M classShortName methodName methodShortName
 * </ul>
 *
 * By default instances of the short forms are represented simply by their
 * integer value. This minimizes log size but makes the log very hard to read by a human.
 * A single character prefix (C, F, T, M) can be generated by setting the
 * system property {@link PREFIX_PROPERTY}.
 *
 * In addition, a repeated id (but not when used as a value) is, by default, passed as
 * {@link #REPEAT_ID}
 */

public abstract class VMATextStoreFormat {

    public static final String PREFIX_PROPERTY = "max.vma.store.txt.shortprefix";
    public static final long REPEAT_ID_VALUE = Long.MIN_VALUE;
    public static final char REPEAT_ID = '*';
    public static final int BATCHED = 1;
    public static final int PER_THREAD = 2;
    public static final int TEXT_KEY = 4;

    /**
     * Log records that do not have a (relative) time component.
     * N.B. {@link Key#INITIALIZE_LOG}, {@link Key#FINALIZE_LOG} and {@link Key.THREAD_SWITCH}
     * are in this set because their time component is always absolute.
     */
    public static final EnumSet<Key> noTimeSet = EnumSet.of(
                    Key.INITIALIZE_STORE, Key.FINALIZE_STORE, Key.THREAD_SWITCH,
                    Key.CLASS_DEFINITION, Key.FIELD_DEFINITION,
                    Key.THREAD_DEFINITION, Key.METHOD_DEFINITION,
                    Key.REMOVAL);

    public static final Map<String, Key> textKeyCommandMap = new HashMap<String, Key>();
    public static final Map<String, Key> codeCommandMap = new HashMap<String, Key>();

    static {
        for (Key key : Key.values()) {
            codeCommandMap.put(key.code, key);
            textKeyCommandMap.put(key.text, key);
        }
    }

    public static Key getCommand(boolean textKeyMode, String code) {
        if (textKeyMode) {
            return textKeyCommandMap.get(code);
        } else {
            return codeCommandMap.get(code);
        }
    }

    public static String getString(boolean textKeyMode, Key key) {
        return textKeyMode ? key.text : key.code;
    }

    public static final char OBJ_VALUE = 'O';
    public static final char LONG_VALUE = 'J';
    public static final char FLOAT_VALUE = 'F';
    public static final char DOUBLE_VALUE = 'D';

    // Argument indices for record component access
    public static final int KEY_INDEX = 0;
    public static final int TIME_INDEX = 1;
    public static final int THREAD_INDEX = 2;
    public static final int BCI_INDEX = 3;
    public static final int OBJ_ID_INDEX = 4;
    public static final int STATIC_FIELDNAME_INDEX = 4;
    public static final int STATIC_CLASSNAME_INDEX = 4;
    public static final int ID_CLASSNAME_INDEX = 5;
    public static final int NEW_CLASSNAME_INDEX = 5;
    public static final int ID_FIELDNAME_INDEX = 5;
    public static final int ID_MEMBERNAME_INDEX = 5;
    public static final int ARRAY_INDEX_INDEX = 5;
    public static final int NEW_ARRAY_LENGTH_INDEX = 5;
    public static final int CONST_LOAD_VALUE_INDEX = 4;
    public static final int ARRAY_LENGTH_INDEX = 5;
    public static final int LOADSTORE_DISP_INDEX = 4;
    public static final int IF_OPCODE_INDEX = 4;
    public static final int OP_OPCODE_INDEX = 4;
    public static final int OP_VALUES_INDEX = 5;
    public static final int CONV_OPCODE_INDEX = 4;
    public static final int RETURN_VALUE_INDEX = 4;
    public static final int RETURN_THROW_POP_INDEX = 5;
    public static final int STACK_ADJUST_INDEX = 4;
    public static final int GOTO_TARGET_INDEX = 4;
    public static final int DEFINE_ARG_INDEX = 1;

    public static boolean hasId(Key code) {
        return code == Key.UNSEEN || hasIdSet.contains(code);
    }

    public static boolean hasTime(Key key) {
        return !noTimeSet.contains(key);
    }

    public static boolean hasTimeAndThread(Key key) {
        return hasTime(key);
    }

    public static boolean hasBci(Key key) {
        return hasBciSet.contains(key);
    }

// START GENERATED CODE
// EDIT AND RUN VMATextStoreFormatGenerator.main() TO MODIFY

    public enum Key {
        THREAD_DEFINITION("T", "0"),
        CLASS_DEFINITION("C", "1"),
        FIELD_DEFINITION("F", "2"),
        METHOD_DEFINITION("M", "3"),
        ADVISE_BEFORE_GET_STATIC("BGS", "4"),
        ADVISE_BEFORE_PUT_STATIC("BPS", "5"),
        ADVISE_BEFORE_CONVERSION("BC", "6"),
        ADVISE_BEFORE_GC("BGC", "7"),
        ADVISE_BEFORE_GET_FIELD("BGF", "8"),
        ADVISE_BEFORE_INSTANCE_OF("BIO", "9"),
        INITIALIZE_STORE("IL", "A"),
        ADVISE_AFTER_GC("AGC", "B"),
        ADVISE_BEFORE_THREAD_TERMINATING("BTT", "C"),
        ADVISE_BEFORE_CHECK_CAST("BCC", "D"),
        ADVISE_BEFORE_RETURN_BY_THROW("BRBT", "E"),
        ADVISE_BEFORE_STACK_ADJUST("BSA", "F"),
        ADVISE_BEFORE_INVOKE_VIRTUAL("BIV", "G"),
        ADVISE_BEFORE_INVOKE_STATIC("BIS", "H"),
        ADVISE_AFTER_MULTI_NEW_ARRAY("AMNA", "I"),
        ADVISE_BEFORE_ARRAY_LOAD("BAL", "J"),
        FINALIZE_STORE("FL", "K"),
        ADVISE_BEFORE_CONST_LOAD("BCL", "L"),
        ADVISE_BEFORE_IF("BI", "M"),
        ADVISE_BEFORE_RETURN("BR", "N"),
        THREAD_SWITCH("ZT", "O"),
        ADVISE_AFTER_NEW_ARRAY("ANA", "P"),
        ADVISE_BEFORE_GOTO("BG", "Q"),
        ADVISE_BEFORE_ARRAY_STORE("BAS", "R"),
        ADVISE_AFTER_ARRAY_LENGTH("AAG", "S"),
        ADVISE_BEFORE_PUT_FIELD("BPF", "T"),
        ADVISE_AFTER_NEW("AN", "U"),
        ADVISE_BEFORE_INVOKE_SPECIAL("BIZ", "V"),
        ADVISE_BEFORE_OPERATION("BO", "W"),
        ADVISE_AFTER_METHOD_ENTRY("AME", "X"),
        ADVISE_BEFORE_THROW("BT", "Y"),
        ADVISE_AFTER_LOAD("AL", "Z"),
        ADVISE_BEFORE_INVOKE_INTERFACE("BII", "a"),
        ADVISE_BEFORE_STORE("BS", "b"),
        ADVISE_BEFORE_MONITOR_EXIT("BMX", "c"),
        REMOVAL("D", "d"),
        ADVISE_BEFORE_LOAD("BL", "e"),
        ADVISE_BEFORE_MONITOR_ENTER("BME", "f"),
        UNSEEN("U", "g"),
        ADVISE_BEFORE_THREAD_STARTING("BTS", "h"),
        ADVISE_AFTER_ARRAY_LOAD("AAL", "i");
        public final String text;
        public final String code;
        private Key(String text, String code) {
            this.text = text;
            this.code = code;
        }
    }

    public static final EnumSet<Key> hasIdSet = EnumSet.of(
        Key.ADVISE_BEFORE_RETURN_BY_THROW,
        Key.ADVISE_AFTER_NEW,
        Key.ADVISE_AFTER_NEW_ARRAY,
        Key.ADVISE_AFTER_MULTI_NEW_ARRAY,
        Key.ADVISE_BEFORE_ARRAY_LOAD,
        Key.ADVISE_BEFORE_ARRAY_STORE,
        Key.ADVISE_BEFORE_GET_FIELD,
        Key.ADVISE_BEFORE_PUT_FIELD,
        Key.ADVISE_BEFORE_INVOKE_VIRTUAL,
        Key.ADVISE_BEFORE_INVOKE_SPECIAL,
        Key.ADVISE_BEFORE_INVOKE_STATIC,
        Key.ADVISE_BEFORE_INVOKE_INTERFACE,
        Key.ADVISE_BEFORE_THROW,
        Key.ADVISE_BEFORE_CHECK_CAST,
        Key.ADVISE_BEFORE_INSTANCE_OF,
        Key.ADVISE_BEFORE_MONITOR_ENTER,
        Key.ADVISE_BEFORE_MONITOR_EXIT,
        Key.ADVISE_AFTER_ARRAY_LOAD,
        Key.ADVISE_AFTER_ARRAY_LENGTH,
        Key.ADVISE_AFTER_METHOD_ENTRY);

    public static final EnumSet<Key> hasBciSet = EnumSet.of(
        Key.ADVISE_AFTER_NEW,
        Key.ADVISE_AFTER_NEW_ARRAY,
        Key.ADVISE_AFTER_MULTI_NEW_ARRAY,
        Key.ADVISE_BEFORE_IF,
        Key.ADVISE_AFTER_LOAD,
        Key.ADVISE_BEFORE_LOAD,
        Key.ADVISE_BEFORE_GOTO,
        Key.ADVISE_BEFORE_CONST_LOAD,
        Key.ADVISE_BEFORE_ARRAY_LOAD,
        Key.ADVISE_BEFORE_STORE,
        Key.ADVISE_BEFORE_ARRAY_STORE,
        Key.ADVISE_BEFORE_CONVERSION,
        Key.ADVISE_BEFORE_RETURN,
        Key.ADVISE_BEFORE_STACK_ADJUST,
        Key.ADVISE_BEFORE_OPERATION,
        Key.ADVISE_BEFORE_GET_STATIC,
        Key.ADVISE_BEFORE_PUT_STATIC,
        Key.ADVISE_BEFORE_GET_FIELD,
        Key.ADVISE_BEFORE_PUT_FIELD,
        Key.ADVISE_BEFORE_INVOKE_VIRTUAL,
        Key.ADVISE_BEFORE_INVOKE_SPECIAL,
        Key.ADVISE_BEFORE_INVOKE_STATIC,
        Key.ADVISE_BEFORE_INVOKE_INTERFACE,
        Key.ADVISE_BEFORE_THROW,
        Key.ADVISE_BEFORE_CHECK_CAST,
        Key.ADVISE_BEFORE_INSTANCE_OF,
        Key.ADVISE_BEFORE_MONITOR_ENTER,
        Key.ADVISE_BEFORE_MONITOR_EXIT,
        Key.ADVISE_AFTER_ARRAY_LOAD,
        Key.ADVISE_AFTER_ARRAY_LENGTH,
        Key.ADVISE_AFTER_METHOD_ENTRY);
// END GENERATED CODE
}
